/**
 * 
 */
package VBox;

import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;

import Core.DB;


/**
 * @author k4rthikR
 *
 */
@SuppressWarnings("unused")
public class Hyperviser {
	
	Logger log = Logger.getAnonymousLogger();
	Job job = new Job();
	private String VDI_SOURCE_DIR;
	private String VDI_TARGET_DIR;
	static String CREATEVMSCRIPT;
	String MYIP;
	String ZEUSPORT;
	String ZEUSIP;
	private String STARTVMSCRIPT;
	private String MODIFYVMSCRIPT;
	private String STOPVMSCRIPT;
	private String DELETEVMSCRIPT;
	DB db = new DB();
	
	
	public Hyperviser(Properties props) {
		VDI_SOURCE_DIR = props.getProperty("vmm.vdisourcedir","/vdi");
		VDI_TARGET_DIR = props.getProperty("vmm.vditargetdir","/clients");
		CREATEVMSCRIPT = props.getProperty("vmm.scriptsdir","./scripts")+"createvm.sh";
		String scriptPath = props.getProperty("vmm.scriptsdir","./scripts");
		STARTVMSCRIPT = scriptPath +"startvm_new.sh";
		STOPVMSCRIPT = scriptPath +"stopvm.sh";
		MODIFYVMSCRIPT = scriptPath +"modifyvm.sh";
		DELETEVMSCRIPT = scriptPath +"deletevm.sh";
		MYIP = props.getProperty("vmm.getvrdpaddress",myIP());
		ZEUSPORT = props.getProperty("vmm.zeusclientport","5000");
		ZEUSIP = props.getProperty("vmm.zeusip","5000");
	}
	
	/**
	 * Shows my IP, however works only for LAN, not WLAN
	 */
	String myIP() {
		  InetAddress ownIP;
		try {
			ownIP = InetAddress.getLocalHost();
		} catch (UnknownHostException e) {
			return "127.0.0.1";
		}
	     return ownIP.getHostAddress();
	}
	
	boolean isVM(String vmname) {
		try {
			Statement st;
			st = db.getCon().createStatement();
			ResultSet res = st.executeQuery("SELECT status FROM  vms where vmname='"+vmname+"'");
        
       while (res.next()) {
          String status = res.getString("status");
          if(status.equalsIgnoreCase("active"))
        		  { log.info("valid Vm"); return true; }
          else return false;
	}
       return false;
	}
	catch (SQLException e) {
		
		log.info(e.getMessage());
	}
		return true;
	}
	
	
	
	/**
	 * Inserts an entry in the DB when creating a new VM
	 * @param vm A VM object
	 * @return Errorcode
	 */
	int insertInDB(VM vm) {
		try {
			Statement st;
			st = db.getCon().createStatement();
			String query = "INSERT INTO vms(vmname,owner,ram,hdd, cores,os) values ('"+vm.name+"' , 1000, "+ vm.ram+" , "+vm.hdd+", "+vm.cores+", '"+vm.os.OSName+"') " ; 
			log.info("Executing : "+query);
			
			int res = st.executeUpdate(query);
        
       log.info("Successfully added entry in DB");
       return 0;
	}
	catch (SQLException e) {
		log.severe("Error updating DB");
		log.severe(e.getMessage());
		
		return -1;
	}
		
	}
	
	/**
	 * Sets status of a VM to 'deleted' in DB
	 * @param vm
	 * @return
	 */
	int deleteFromDB(VM vm) {
		try {
			Statement st;
			st = db.getCon().createStatement();
			String query = "UPDATE vms SET status='deleted' where vmname='"+vm.name+"'" ;
			log.info("Executing : "+query);
			
			int res = st.executeUpdate(query);
        
       log.info("Successfully updated entry in DB");
       return 0;
	}
	catch (SQLException e) {
		log.severe("Error updating DB");
		return -1;
	}
		
	}
	
	
	int insertVM(VM vm) {
		try {
			Statement st;
			st = db.getCon().createStatement();
			
			int res = st.executeUpdate("INSERT INTO vms('vmname','owner','ram','hdd', 'cores','os') values ('"+vm.name+"' , "+vm.owner+", "+ vm.ram+" , "+vm.hdd+", "+vm.cores+", '"+vm.os.OSName+"') " );
        
       log.info("Successfully added entry in DB");
       return 0;
	}
	catch (SQLException e) {
		log.severe("Error updating DB");
		return -1;
	}
		
	}
	
	public int createVM(VM vm) {
		//TODO GENERATE VRDP Port, Address, My IP
		//TODO 
		int vrdpPort = 3000;
		
		if(isVM(vm.name)) {
				log.info("A VM already exists with this name.. Aborting..");
				return -1;
				
		}
		String osVdi = VDI_SOURCE_DIR+File.separator+vm.os.OSName+".vdi";
		String cmd=CREATEVMSCRIPT+" "+vm.name+" "+vm.os.OSName+" "+vm.ram+" "+vrdpPort+" "+ MYIP +" "+ osVdi + " " + VDI_TARGET_DIR + " " +  vm.hdd + " " + vm.cores + " " + ZEUSPORT;
		
		log.info("CreateVM : Going to execute "+cmd);
		
		VMTask task = new VMTask();
		task.cmd = cmd;
		
		//int ret = task.call();
		int ret = job.add("CREATE", vm);
		
		if(ret == 0)
			{
			//log.info("Succesfully created VM "+vm.name);
			//insertInDB(vm);
			}
		else
			log.severe("Error creating VM "+vm.name);
		return ret;
	}
	public void startVM(VM vm){
		String name = vm.name;
		if(isVM(name) == false) {
			log.info("Invalid VM Name.. Aborting..");
			return;
	}
		
		
		log.info("Starting VM "+name);
		VMTask task = new VMTask();
		//String cmd = STARTVMHEADLESS.replaceFirst("VMNAME",name);
		//String cmd = Hyperviser.Commands.STARTVMHEADLESS.replaceFirst("VMNAME",name);
		PortForward pf = new PortForward();
	//	String cmd = STARTVMSCRIPT+ " "+ name+" "+pf.getPort(vm)+ " 22 &";
		String cmd = STARTVMSCRIPT+ " "+ name+" "+pf.getPort(vm.name,"100")+ " "+ZEUSPORT+" "+ZEUSIP+" &";
		task.cmd = cmd;
		//Thread newVMThread = new Thread(task);
		//newVMThread.start();
		
		//ExecutorService pool = Executors.newFixedThreadPool(1);
		//pool.submit(task);
		log.info("cmd : "+cmd);
		job.add("START", vm);
		log.info("\t Task submitted to thread");
	}
	
	public void stopVM(VM vm){
		String name = vm.name;
		if(isVM(name) == false) {
			log.info("Invalid VM Name.. Aborting..");
			return;
	}
		log.info("Stoping VM "+name);
		VMTask task = new VMTask();
		String cmd = STOPVMSCRIPT+ " "+ name;
		task.cmd = cmd;
		PortForward pf = new PortForward();
		pf.unreserve(vm.name);
		//task.call();
		job.add("STOP", vm);
		//Thread newVMThread = new Thread(task);
		//newVMThread.start();
		log.info("\t Task submitted to thread");
	}
	
	public int modifyVM(VM vm){
		
		if(isVM(vm.name) == false) {
			log.info("Invalid VM Name.. Aborting..");
			return -1;
	}
		String cmd=MODIFYVMSCRIPT+" "+vm.name+" "+vm.ram+" "+  vm.hdd + " " + vm.cores+" "+VDI_SOURCE_DIR;
		
		log.info("modifyVM : Going to execute "+cmd);
		
		VMTask task = new VMTask();
		task.cmd = cmd;
		//int ret = task.call();
		int ret = job.add("MODIFY",vm);
		if(ret == 0)
			log.info("Succesfully modified VM "+vm.name);
		else
			log.severe("Error modifying VM "+vm.name);
		return ret;
		
	}
	
	public void deleteVM(VM vm) {
		if(isVM(vm.name) == false) {
			log.info("Invalid VM Name.. Aborting..");
			return;
	}
		String name = vm.name;
		log.info("Deleting VM "+name);
		VMTask task = new VMTask();
		String cmd = DELETEVMSCRIPT+ " "+ name;
		task.cmd = cmd;
		job.add("DELETE",vm);
		//task.call();
		//Thread newVMThread = new Thread(task);
		//newVMThread.start();
		log.info("\t Task submitted to thread");
	}
	

	
	class Commands {
	/** Requires VMNAME, OSNAME */
	static final String CREATECMD="VBoxManage createvm --name VMNAME --ostype OSNAME --register";
	/** Requires VMNAME, VMRAM, VMVRDPPORT, VMVRDPADDRESS */
	static final String SETPARAMS="VBoxManage modifyvm VMNAME --memory VMRAM --nic1 nat --macaddress1 AABBCCDDEEFF --vrdp on --vrdpport VMVRDPPORT --vrdpaddress VMVRDPADDRESS --vrdpvideochannelquality 50";
	/** Requires HDDLOCATION, HDDSIZE */
	static final String CREATEHDD="VBoxManage createhd --filename HDDLOCATION --size HDDSIZE --register";
	/** Requires VMNAME */
	static final String SETSTORAGECTL="VBoxManage storagectl VMNAME --name 'IDE Controller' --add ide";
	/** Requires VMNAME, HDDLOCATION */
	static final String SETHDD="VBoxManage modifyvm VMNAME --hda HDDLOCATION";
	/** Requires VMNAME */
	static final String SETBOOT="VBoxManage modifyvm VMNAME --boot1 dvd --boot2 disk";
	/** Requires VMNAME */
	static final String STARTVM="VBoxManage startvm VMNAME";
	/** Requires VMNAME */
	static final String STARTVMHEADLESS="VBoxHeadless --startvm VMNAME &";
	static final String STOPVM="";
	static final String DELETEVM="";
	
	}
	
	/**
	 * Creates and registers a VM for a user.
	 * @param vmId Id for a VM
	 * @param vm The VM object that has the config
	 */
	/*
	public int createVM(VM vm){
		
		
		VMTask task = new VMTask();
		String vmName = vm.name;
		log.info("Creating VM with name "+vmName);
		String cmd = CREATECMD.replaceFirst("VMNAME", vmName);
		cmd = cmd.replaceFirst("OSNAME", vm.os.OSName);
		task.cmd = cmd;
		//String cmd = CREATECMD.replaceFirst("VMNAME", vm.name);
		task.call();
		
		cmd = SETPARAMS.replaceFirst("VMNAME", vmName);
		cmd = cmd.replaceFirst("VMRAM", vm.ram+"");
		
		
		cmd = cmd.replaceFirst("VMVRDPPORT", "1000").replaceFirst("VMVRDPADDRESS", "192.168.2.2");
		task.cmd = cmd;
		task.call();
		
		String hddLocationTemp = "/tmp/"+vmName+".vdi"; //TODO Remove Temporary HDD Location
		cmd = CREATEHDD.replaceFirst("HDDLOCATION", hddLocationTemp).replaceFirst("HDDSIZE", vm.hdd+"");
		task.cmd = cmd;
		task.call();
		
		cmd = SETSTORAGECTL.replaceFirst("VMNAME", vmName);
		task.cmd = cmd;
		task.call();
		
		cmd = SETHDD.replaceFirst("VMNAME", vmName).replaceFirst("HDDLOCATION", hddLocationTemp);
		task.cmd = cmd;
		task.call();
		
		cmd = SETBOOT.replaceFirst("VMNAME", vmName);
		task.cmd = cmd;
		task.call();
		
		log.info("End of VM Creation");
		return 0;
	}
	*/
}
